using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.CSharp;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Gj
{
    public class FileTools
    {
        public static void LoadScript(string path, string type)
        {
#if UNITY_EDITOR && !UNITY_SERVER
            TryLoadCompiledType(CompileSource(File.ReadAllText(path)), type);
#endif
        }

        public static string CreateFolder(string path, string name)
        {
            return CreateFolder(Path.Combine(path, name));
        }

        public static string CreateFolder(string path)
        {
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            return path;
        }

        public static void SaveFile(string path, string info, bool append = false)
        {
            StreamWriter sw;
            var t = new FileInfo(path);
            if (!t.Exists)
            {
                sw = t.CreateText();
            }
            else
            {
                if (append)
                    sw = t.AppendText();
                else
                    return;
            }

            sw.WriteLine(info);
            sw.Close();
            sw.Dispose();
        }

        public static void SaveByteFile(string path, byte[] info, bool append = false)
        {
            var dir = Path.GetDirectoryName(path);
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            FileStream sw;
            var t = new FileInfo(path);
            if (!t.Exists)
            {
                sw = t.Create();
            }
            else
            {
                if (append)
                {
                    sw = t.OpenWrite();
                    sw.SetLength(0);
                }
                else
                {
                    return;
                }
            }

            sw.Write(info);
            sw.Close();
            sw.Dispose();
        }

        public static ArrayList LoadFile(string path)
        {
            StreamReader sr = null;
            try
            {
                sr = File.OpenText(path);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                return null;
            }

            string line;
            var arrlist = new ArrayList();
            while ((line = sr.ReadLine()) != null) arrlist.Add(line);
            sr.Close();
            sr.Dispose();
            return arrlist;
        }

        public static byte[] LoadByteFile(string path)
        {
            StreamReader sr = null;
            try
            {
                sr = File.OpenText(path);
            }
            catch (Exception e)
            {
                return null;
            }

            var br = new BinaryReader(sr.BaseStream);
            br.BaseStream.Position = 0;
            var result = br.ReadBytes((int)sr.BaseStream.Length);
            sr.Close();
            sr.Dispose();
            br.Close();
            return result;
        }

        public static int AllFileSize(string path)
        {
            var sum = 0;
            if (!Directory.Exists(path)) return 0;

            var dti = new DirectoryInfo(path);

            var fi = dti.GetFiles();

            foreach (var f in fi) sum += Convert.ToInt32(f.Length / 1024);

            var di = dti.GetDirectories();

            if (di.Length > 0)
                for (var i = 0; i < di.Length; i++)
                    sum += AllFileSize(di[i].FullName);
            return sum;
        }

        public static int FileSize(string path, string name)
        {
            var sum = 0;
            if (!Directory.Exists(path))
            {
                return 0;
            }

            var Files = new FileInfo(path + name);
            sum += Convert.ToInt32(Files.Length / 1024);
            return sum;
        }

        public static void DeleteFile(string path, string name)
        {
            File.Delete(Path.Combine(path, name));
        }

        public static void DeleteFolder(string dir)
        {
            if (Directory.Exists(dir)) //如果存在这个文件夹删除之
            {
                foreach (var d in Directory.GetFileSystemEntries(dir))
                    if (File.Exists(d))
                        File.Delete(d); //直接删除其中的文件                       
                    else
                        DeleteFolder(d); //递归删除子文件夹
                Directory.Delete(dir, true); //删除已空文件夹                
            }
        }

        public static void MoveFile(string source, string dest, Action<string> callback = null)
        {
            Debug.Log(source + " -> " + dest);
            if (Directory.Exists(source))
            {
                if (Directory.Exists(dest)) Directory.Delete(dest, true);
                var di = Directory.CreateDirectory(dest);
                // DirectoryInfo folder = new DirectoryInfo(source);
                var files = Directory.GetFileSystemEntries(source);

                foreach (var file in files)
                {
                    if (Directory.Exists(file))
                        MoveFile(file, Path.Combine(di.FullName, Path.GetFileName(file)));
                    else
                    {
                        File.Move(file, Path.Combine(di.FullName, Path.GetFileName(file)));
                    }
                    if (callback != null) callback(file);
                }

                DeleteFolder(source);
            }
            else if (File.Exists(source))
            {
                var dir = Directory.GetParent(dest);
                if (File.Exists(dest)) File.Delete(dest);
                Directory.CreateDirectory(dir.FullName);
                File.Move(source, dest);
                File.Delete(source);
            }
        }

        public static void CopyFile(string source, string dest, Action<string> callback = null)
        {
            Debug.Log(source + " -> " + dest);
            if (Directory.Exists(source))
            {
                if (Directory.Exists(dest)) Directory.Delete(dest, true);
                var di = Directory.CreateDirectory(dest);
                // DirectoryInfo folder = new DirectoryInfo(source);
                var files = Directory.GetFileSystemEntries(source);

                foreach (var file in files)
                {
                    if (Directory.Exists(file))
                        CopyFile(file, Path.Combine(di.FullName, Path.GetFileName(file)));
                    else
                        File.Copy(file, Path.Combine(di.FullName, Path.GetFileName(file)));
                    if (callback != null) callback(file);
                }
            }
            else if (File.Exists(source))
            {
                var dir = Directory.GetParent(dest);
                if (File.Exists(dest)) File.Delete(dest);
                Directory.CreateDirectory(dir.FullName);
                var info = new FileInfo(source);
                info.CopyTo(dest);
                // File.Delete(source);
            }
        }

        /// <summary>
        ///     序列化
        /// </summary>
        /// <param name="data">要序列化的对象</param>
        /// <returns>返回存放序列化后的数据缓冲区</returns>
        public static byte[] Serialize(object data)
        {
            var formatter = new BinaryFormatter();
            var rems = new MemoryStream();
            formatter.Serialize(rems, data);
            return rems.GetBuffer();
        }

        /// <summary>
        ///     反序列化
        /// </summary>
        /// <param name="data">数据缓冲区</param>
        /// <returns>对象</returns>
        public static object Deserialize(byte[] data)
        {
            var formatter = new BinaryFormatter();
            var rems = new MemoryStream(data);
            return formatter.Deserialize(rems);
        }
#if UNITY_EDITOR && !UNITY_SERVER
        public static string LoadText(string path)
        {
            var t = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset));
            if (t == null) return "";
            return (t as TextAsset).text;
        }

        public static CompilerResults CompileSource(string sourceCode)
        {
            var csc = new CSharpCodeProvider(
                new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });

            var referencedAssemblies =
                AppDomain.CurrentDomain.GetAssemblies()
                    .Where(a => !a.FullName.StartsWith("mscorlib", StringComparison.InvariantCultureIgnoreCase))
                    .Where(a => !a
                        .IsDynamic) //necessary because a dynamic assembly will throw and exception when calling a.Location
                    .Select(a => a.Location)
                    .ToArray();

            var parameters = new CompilerParameters(
                referencedAssemblies);

            return csc.CompileAssemblyFromSource(parameters,
                sourceCode);
        }

        public static object TryLoadCompiledType(CompilerResults compilerResults, string typeName,
            params object[] constructorArgs)
        {
            if (compilerResults.Errors.HasErrors)
            {
                Debug.LogError("Can not TryLoadCompiledType because CompilerResults.HasErrors");
                return null;
            }

            var type = compilerResults.CompiledAssembly.GetType(typeName);

            if (null == type)
            {
                Debug.LogError("Compiled Assembly does not contain a type [" + typeName + "]");
                return null;
            }

            return Activator.CreateInstance(type, constructorArgs);
        }

#endif
    }
}